from enemies import Invader,InvaderStalker,InvaderHunter,InvaderMine,HomingMover,PathMover,GravityMover,InvaderMover,InvaderInvertedMine
import pygame
from common import load_image,load_sound,ViewPort,Tick
from pygame.locals import DOUBLEBUF,FULLSCREEN,HWSURFACE,K_ESCAPE,K_SPACE,QUIT,KEYDOWN,K_y,K_n

def getFreeTilePositions(ge,safe_zones,sz_size=(5,5)):
    '''
    Generate all (row,col) indexes in a map that are not blocked.
    @ge: the Game engine.
    @safe_zones: True to not include positions in players safe zones.
    @sz_size: the size of the safe zones
    '''
    safe_zones = []
    for p in ge.players.sprites():
        rowi,coli = ge.game_map.getMapXYAtPos(*p.startingPos)
        safe_zones.append(pygame.Rect(rowi,coli,0,0).inflate(*sz_size))
    m = ge.game_map.map
    def freespace(x,y):
        # make sure tile has at least 1 free space in every direction
        if m[y][x] or m[y+1][x] or m[y-1][x] or m[y][x+1] or m[y][x-1]:
            return False
        else:
            return True
    for rowi,row in enumerate(ge.game_map.map):
        for coli,col in enumerate(row):
            if rowi < 2 or coli < 2:
                continue
            if freespace(rowi,coli) and not any(safez for safez in safe_zones if safez.collidepoint(rowi,coli)):
                yield (rowi,coli)

class GameMode(object):
    def __init__(self,game):
        self.game = game

    def setup(self,settings):
        settings = self.game.settings
        self.settings = settings
        screen = pygame.display.get_surface()
        #screen = self.game.screen
        ge = self.game.ge
        self.ge = ge 
        self.players = self.game.createPlayers(settings,ge)
        self.viewports = self.game.initViewPorts(screen,len(self.players))
        #-------------------------------------
        # Setup game graphics
        parallax_layers = settings['graphics']['parallax_layers']
        if len(self.players) > 0:
            self.forground_layer = self.game.map_loader.getTiledLayer(parallax_layers)

        self.max_fps = settings['graphics']['max_fps']
        self.font = pygame.font.Font(None, self.viewports[0].width*0.04)

        self.ambience_sound = self.game.setupSound(settings['sound'])
        # Start Ambience sound
        #ambience_sound.play(-1)

        self.bg = ge.game_map.bgcolor
        self.status_bg = (240,240,240)

        self.player_view = []
        for nr in range(0,len(self.players)):
            self.player_view.append((self.players[nr],self.viewports[nr]),)

    def showScore(self,player,viewport,alldone=False):
        '''Show player score'''
        self.game.stopSound()
        if alldone:
            info_string = u"JUBEL! JUBEL!\nDU VANN!\nScore: %s\nEnemies left %d"%(player.score,len(self.ge.enemies))
        elif player.lives < 1 and len(self.ge.enemies):
            info_string = u"GAME OVER DIN DUMME FJANT!\nScore: %s\nEnemies left %d"%(player.score,len(self.ge.enemies))
        elif player.lives and len(self.ge.enemies):
            info_string = u"DIN SILLMJ\u00F6LKE!!!\nScore: %s\nEnemies left %d"%(player.score,len(self.ge.enemies))
        else:
            info_string = u"BANAN KLARAD!\nScore: %s\nEnemies left %d\n"%(player.score,len(self.ge.enemies))
            
        self.game.displayMessage(info_string,viewport=viewport)
            
#    def startGame(self,settings):
#        self.game.settings = settings
#        nc = self.game.net_controller
#        self.game.screen = self.game.initDisplay(settings)
#        self.game.game_map = self.game.initMap(settings['game']['map'])
#        self.game.log.write("STARTING No network game\n")
#        self.setup(self,settings)
#        self.game._runGame()

class DeathMatch(GameMode):
    
    def runGame(self):
        '''Game loop'''
        self.game.setupGe(self.game.settings['game']['map'])
        self.setup(self.game.settings)
        settings = self.game.settings
        
        screen = self.game.screen
        ge = self.game.ge
        nc = self.game.net_controller

        ge.begin()
        # update with net messages before we add our own game objects and players
        nc.update()
        self.delay = self.game.delay # time to sleep between every frame

        # add local players
        for player in self.players:
            ge.addPlayer(player)
        
        if not len(self.players):
            self.game.displayMessage("Running")

        clock = self.game.clock
        clock.tick()
        fps_tick = Tick(2.0)
            
        #------------------- Main Game loop -----------------
        while (ge.running and ge.net_controller.isAlive()):
            # calculate time since last update 
            dt = clock.tick(self.max_fps)/1000.0


    
            # Manage Player and ship lifes
            for player in ge.players.sprites():
                # Count down life
                if player.ship.life.val <= 0:
                    ge.shipKilled(player.ship)
                    player.lives -= 1
                    if player.lives < 1 :
                        break
                    player.ship.reset()
    
                # Die if landed and not on a tile base
                if player.ship.alive() and player.ship.landed:
                    if not player.isDocked(dt):
                        player.lives -= 1
                        ge.shipKilled(player.ship)
                        print "Oops cant land there I killed you!(not a base tile) "
                # respawn player
                if not player.ship.alive():
                    if player.lives > 0:
                        ge.respawnPlayer(player,dt)
    
            # update my self with net messages
            nc.update()
            # send keep alive ping
            nc.ping(dt)

            keystate = pygame.key.get_pressed()

            # Check if quit
            if self.game.checkQuit(keystate,screen,clock,dt):
                self.game.quit()
            
            # update main game engine
            ge.update(dt,keystate)

            # print fps info
            fps_tick.tick(dt)
            if fps_tick.isSet():
                fps_tick.reset()
                print "FPS:%d"%(clock.get_fps())

            # let operating system breath 
            pygame.time.wait(self.delay)

            # dont draw anything if we dont have any local players(dedicated server)
            if not self.players:
                continue

            # Clear screen 
            screen.fill(self.bg)

            # ------------- Draw game on screen ------------------
            for player,viewport in self.player_view:
                # Update viewport position 
                (viewport.centerx, viewport.centery) = ge.getPlayerPos(player)

                # Render layers 
                self.forground_layer.render(viewport)
                    
                ge.all_sprites.draw(viewport)

                # draw player status
                self.game.drawStatus(viewport,player,self.font,self.status_bg)

                if player.selecting:
                    player.ws.render(viewport)

                # draw viewport splitter line
                screen.fill((200,200,200),(0,viewport.height,viewport.width,2))
            # make changes visible 
            pygame.display.flip()

        self.showScore()

        # wait a bit before event quit check (to show score)
        pygame.time.wait(500)
        pygame.event.clear()          
        while True:
            event = pygame.event.wait()
            if event.type == QUIT or (event.type == KEYDOWN and event.key in (K_SPACE,K_ESCAPE)):
                #net_controller.close() # close net connection
#                pygame.time.wait(500)                 
                pygame.time.wait(100)               
                pygame.event.clear()           
                return True

    def showScore(self):
        '''Show player score'''
        # show score
        alive_players = [p for p in self.players if p.lives>0]
        for player,viewport in self.player_view:
            self.game.stopSound()
            if len(alive_players) > 1:
                info_string = u"SPEL AVBRUTET!!!\nScore: %s"%(player.score)
            elif player.lives < 1:
                info_string = u"SNESEGLARE!!!\nScore: %s"%(player.score)
            else:
                info_string = u"VINNARE!!\nJUBEL JUBEL!!\nScore: %s"%(player.score)
                
            self.game.displayMessage(info_string,viewport=viewport)

class GameEvent(Exception):
    pass
class GameOver(GameEvent):
    pass
class LevelDone(GameEvent):
    pass
class GameCompleted(GameEvent):
    pass

class OnePlayer(GameMode):
    maps = [{'map':'level1','enemies':3},{'map':'level1','enemies':5},{'map':'level1','enemies':10},{'map':'turb_test','enemies':20},{'map':'turb_test','enemies':30},{'map':'turb_test','enemies':50}]
    def runGame(self):
        ''' Main game loop'''
        self.maps_iter = iter(self.maps)
        while True:
            
            try:
                self.nextLevel()
            except GameCompleted:
                self.game.screen.fill(self.bg)
                self.levelDone(alldone=True)
                break
            try:
                self.startLevel()
            except GameOver:
                self.gameOver()
                break
            except LevelDone:
                self.levelDone()
                continue
            else:
                break

    def nextLevel(self):
        try:
            self.game_map = self.maps_iter.next()
        except StopIteration:
            raise GameCompleted()
        self.game.setupGe(self.game_map['map'])
        print "GAME MAP:",self.game_map
        self.setup(self.game.settings)
        
        screen = self.game.screen
        ge = self.game.ge
        nc = self.game.net_controller

        ge.begin()
        # update with net messages before we add our own game objects and players
        nc.update()


        self.delay = self.game.delay # time to sleep between every frame

        # add local players
        for player in self.players:
            ge.addPlayer(player)
        
        import random
        # setup enemy movers dict for this game
        import enemies
        enemies.movers = {}
        enemies.movers[InvaderStalker] = HomingMover(ge)
        enemies.movers[InvaderHunter] = PathMover(ge)
        enemies.movers[InvaderMine] = GravityMover(ge)
        enemies.movers[InvaderInvertedMine] = GravityMover(ge,repell=True,both=True,force=1.5)

        homing_mover = HomingMover(ge)
        path_mover = PathMover(ge)
        gravity_mover = GravityMover(ge)
        #invader_mover = InvaderMover(ge)
        movers = [homing_mover,path_mover,gravity_mover]
        invaders = [InvaderMine,InvaderStalker,InvaderHunter]
                        
        open_tile_positons = list(getFreeTilePositions(ge,True,sz_size=(15,15)))
            
        for nr in range(0,self.game_map['enemies']):
            x,y = random.choice(open_tile_positons)
            x *= ge.game_map.tile_w
            y *= ge.game_map.tile_h
            # add enemies
            #mover =  gravity_mover
#            mover = random.choice(movers)
            invader_cls = random.choice(invaders)
            #mover = path_mover
            #mover = None
            enemy = invader_cls(ge, x, y, vel_x=0, vel_y=0)
#            enemy = InvaderNormal3(ge, x, y, vel_x=0, vel_y=0,mover=mover)
#            enemy = Invader(ge, x, y, vel_x=ge.relative_speed*dx, vel_y=ge.relative_speed*dy,mover=mover)
            ge.addEnemy(enemy)

        for nr in range(0,0):
            x,y = 7+nr*2,7
            x *= ge.game_map.tile_w
            y *= ge.game_map.tile_h
            # add enemies
            #enemy = InvaderStalker(ge, x, y, vel_x=0, vel_y=0)
            enemy = InvaderMine(ge, x, y, vel_x=0, vel_y=0)
            ge.addEnemy(enemy)
    
    def startLevel(self):
        screen = self.game.screen
        ge = self.game.ge
        nc = self.game.net_controller

        clock = self.game.clock
        clock.tick()
        fps_tick = Tick(2.0)
            
        #------------------- Main Game loop -----------------
        while (ge.running and ge.net_controller.isAlive()):
            # calculate time since last update 
            dt = clock.tick(self.max_fps)/1000.0
            keystate = pygame.key.get_pressed()

            # update main game engine
            ge.update(dt,keystate)

    
            # Manage Player and ship lifes
            for player in ge.players.sprites():
                if player.ship.life.val <= 0:
                    ge.shipKilled(player.ship)
                    player.lives -= 1
                    if player.lives < 1 :
                        raise GameOver('Player died!')
                    player.ship.reset()
    
                # Die if landed and not on a tile base
                if player.ship.alive() and player.ship.landed:
                    if not player.isDocked(dt):
                        player.lives -= 1
                        ge.shipKilled(player.ship)
                        print "Oops cant land there I killed you!(not a base tile) "
                # respawn player
                if not player.ship.alive():
                    if player.lives > 0:
                        ge.respawnPlayer(player,dt)
    
            if not ge.enemies:
                # no enemies left stop game
                raise LevelDone('Level completed!')

            # Manage Player and ship lives
            for enemy in ge.enemies.sprites():
                # Count down life
                if enemy.collide:
                    self.ge._manageGoMapCollide(enemy)
                else:
                    enemy.collide_dirs = 0

                if enemy.life.val <= 0:
                    ge.enemyKilled(enemy)




            # update my self with net messages
            nc.update()
            # send keep alive ping
            nc.ping(dt)


            # Check if quit
            if self.game.checkQuit(keystate,screen,clock,dt):
                self.game.quit()
            

            # print fps info
            fps_tick.tick(dt)
            if fps_tick.isSet():
                fps_tick.reset()
                print "FPS:%d"%(clock.get_fps())

            # let operating system breath 
            pygame.time.wait(self.delay)

            # Clear screen 
            screen.fill(self.bg)

            # ------------- Draw game on screen ------------------
            for player,viewport in self.player_view:
                # Update viewport position 
                (viewport.centerx, viewport.centery) = ge.getPlayerPos(player)

                # Render layers 
                self.forground_layer.render(viewport)
                    
                ge.all_sprites.draw(viewport)

                # draw player status
                self.game.drawStatus(viewport,player,self.font,self.status_bg)

                if player.selecting:
                    player.ws.render(viewport)

                # draw viewport splitter line
                screen.fill((200,200,200),(0,viewport.height,viewport.width,2))
            # make changes visible 
            pygame.display.flip()
            
    def levelDone(self,alldone=False):
        # show score
        for player,viewport in self.player_view:
            self.showScore(player,viewport,alldone)
        self._waitInput()

    def gameOver(self):
        # show score
        for player,viewport in self.player_view:
            self.showScore(player,viewport)
        self._waitInput()

    def _waitInput(self):
        # wait a bit before event quit check (to show score)
        pygame.time.wait(500)
        pygame.event.clear()          
        while True:
            event = pygame.event.wait()
            if event.type == QUIT or (event.type == KEYDOWN and event.key in (K_SPACE,K_ESCAPE)):
                #net_controller.close() # close net connection
#                pygame.time.wait(500)                 
                pygame.time.wait(100)               
                pygame.event.clear()           
                return True
